package category

import (
	"HeadLineNews/pkg/db/store"
	"HeadLineNews/pkg/snow"
	"HeadLineNews/pkg/zaplog"
	models "HeadLineNews/proto_models/manage/category_model"
	"HeadLineNews/resp"
	"HeadLineNews/utils/gin_ctx"
	"database/sql"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/jmoiron/sqlx"
)

var (
	errCategoryHasExists = errors.New("该分类已存在")
	errCategoryNotExists = errors.New("该分类不存在")
)

type Repo interface {
	CheckUniqueCategoryName(c *gin.Context, categoryName string) (err error)
	InsertCategory(c *gin.Context, p *models.RequestAddCategory) (err error)

	CheckCategoryExists(c *gin.Context, categoryId string) (categoryName string, err error)
	UpdateCategory(c *gin.Context, p *models.RequestUpdateCategory) (err error)

	RetrieveCategory(c *gin.Context) (p *models.ResponseRetrieveCategory, err error)
	ListCategory(c *gin.Context, p *models.RequestListCategory) (data *models.ResponseListCategory, err error)
}

type repo struct {
	db *sqlx.DB
}

func NewRepo() Repo {
	return &repo{store.DB()}
}

// CheckUniqueCategoryName 检查唯一分类名称
func (r *repo) CheckUniqueCategoryName(c *gin.Context, categoryName string) (err error) {
	sqlStr := `SELECT COUNT(*) FROM app__news_category WHERE category_name=?`
	var count int64
	if err = r.db.Get(&count, sqlStr, categoryName); err != nil {
		zaplog.L().Error("query category count by name failed", zaplog.Fields(c, categoryName, err)...)
		resp.ServerBusy(c)
		return
	}

	if count > 0 {
		err = errCategoryHasExists
		zaplog.L().Warn("category has exists", zaplog.Fields(c, categoryName, nil)...)
		resp.UnprovedParam(c, err.Error())
		return
	}
	return
}

// InsertCategory 将分类保存到数据库
func (r repo) InsertCategory(c *gin.Context, p *models.RequestAddCategory) (err error) {
	sqlStr := `INSERT INTO app__news_category(
            category_id, category_name, 
            create_by, last_update_by, create_time, last_update_time) VALUES(?,?,?,?,NOW(),NOW())`
	if _, err = r.db.Exec(sqlStr,
		snow.GetID(), p.CategoryName, c.GetString(gin_ctx.UserId), c.GetString(gin_ctx.UserId),
	); err != nil {
		zaplog.L().Error("insert category failed", zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	return
}

// CheckCategoryExists 根据 id 检查分类是否存在
func (r *repo) CheckCategoryExists(c *gin.Context, categoryId string) (categoryName string, err error) {
	sqlStr := `SELECT category_name FROM app__news_category WHERE category_id=?`
	if err = r.db.Get(&categoryName, sqlStr, categoryId); err != nil {
		if err == sql.ErrNoRows {
			zaplog.L().Warn("no category was selected by id", zaplog.Fields(c, categoryId, nil)...)
			resp.UnprovedParam(c, errCategoryNotExists.Error())
			return
		}
		zaplog.L().Error("query category by id failed", zaplog.Fields(c, categoryId, err)...)
		resp.ServerBusy(c)
		return
	}
	return
}

// UpdateCategory 更新分类
func (r *repo) UpdateCategory(c *gin.Context, p *models.RequestUpdateCategory) (err error) {
	query := `UPDATE app__news_category`
	var args []interface{}
	query += ` SET last_update_time=NOW(),last_update_by=?`
	args = append(args, c.GetString(gin_ctx.UserId))
	if p.CategoryName != "" {
		query += `,category_name=?`
		args = append(args, p.CategoryName)
	}
	sqlStr := query + ` WHERE category_id=?`
	args = append(args, p.CategoryId)

	if _, err = r.db.Exec(sqlStr, args...); err != nil {
		zaplog.L().Error("update category failed", zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	return
}

// RetrieveCategory 检索单条分类
func (r *repo) RetrieveCategory(c *gin.Context) (p *models.ResponseRetrieveCategory, err error) {
	categoryId := c.Param("categoryId")
	p = new(models.ResponseRetrieveCategory)
	sqlStr := `SELECT c.category_id, c.category_name, c.create_time, c.last_update_time,
       		a1.admin_name AS create_by, a2.admin_name AS last_update_by
			FROM app__news_category c, manage__admin a1, manage__admin a2
			WHERE c.create_by=a1.admin_id
			AND c.last_update_by=a2.admin_id
			AND c.category_id=?`
	if err = r.db.Get(p, sqlStr, categoryId); err != nil {
		if err == sql.ErrNoRows {
			err = errCategoryNotExists
			zaplog.L().Warn("no category was selected by id", zaplog.Fields(c, categoryId, nil)...)
			resp.UnprovedParam(c, err.Error())
			return
		}
		zaplog.L().Error("query category by id failed", zaplog.Fields(c, categoryId, err)...)
		resp.ServerBusy(c)
		return
	}
	return
}

// ListCategory 批量返回分类结果
func (r *repo) ListCategory(c *gin.Context, p *models.RequestListCategory) (data *models.ResponseListCategory, err error) {
	data = new(models.ResponseListCategory)
	records := make([]*models.ACategory, 0, p.PageSize.Size)
	query := `SELECT category_id, category_name FROM app__news_category WHERE TRUE`
	var args []interface{}
	if p.CategoryId != "" {
		query += ` AND category_id=?`
		args = append(args, p.CategoryId)
	}
	if p.CategoryName != "" {
		query += ` AND category_name=?`
		args = append(args, p.CategoryName)
	}
	if p.TimeRange.TimeRangeStart != "" && p.TimeRange.TimeRangeEnd != "" {
		query += ` AND create_time BETWEEN ? AND ?`
		args = append(args, p.TimeRange.TimeRangeStart)
		args = append(args, p.TimeRange.TimeRangeEnd)
	}
	sqlStr := query + ` LIMIT ? OFFSET ?`
	args = append(args, p.PageSize.Size)
	args = append(args, (p.PageSize.Page-1)*p.PageSize.Size)
	if err = r.db.Select(&records, sqlStr, args...); err != nil {
		zaplog.L().Error("query category failed", zaplog.Fields(c, args, err)...)
		resp.ServerBusy(c)
		return
	}
	var count int64
	sqlStr = fmt.Sprintf(`SELECT COUNT(*) FROM (%s) AS alias`, query)
	if err = r.db.Get(&count, sqlStr, args[:len(args)-2]...); err != nil {
		zaplog.L().Error("query category count failed", zaplog.Fields(c, args, err)...)
		resp.ServerBusy(c)
		return
	}
	data.Count = count
	data.Records = records
	return
}

